
# 多人聊天室小项目
import threading #引入线程的包，因为每个客户端发消息就代表了一个线程
import time # 引入时间的包，需要记录发送消息的时间，和时间类的操作
from time import localtime

import wx # wxPython Python的图形化界面库
from socket import socket,AF_INET,SOCK_STREAM # 网络编程的套接字，网络协议，TCP协议
# 继承wx.Frame窗体
class Hhtserver(wx.Frame):
    def __init__(self,name):
        # 依次是父组件，id,标题，位置，大小
        wx.Frame.__init__(self,None,id=1002,title=name+"的服务器",pos=wx.DefaultPosition,size=(600,550))
        # 生成一个面板
        pl = wx.Panel(self)
        # 创建盒子 参数的垂直布局 vertical:垂直
        box = wx.BoxSizer(wx.VERTICAL)
        # 可伸缩的网格布局 水平布局
        fgz1 = wx.FlexGridSizer(wx.HSCROLL)
        # 创建三个按钮
        start_server_btn = wx.Button(pl, size=(200, 40), label='启动服务')
        save_btn = wx.Button(pl, size=(200, 40), label='保存聊天记录')
        stop_btn = wx.Button(pl, size=(200, 40), label='停止服务')
        #添加进网格布局中
        fgz1.Add(start_server_btn)
        fgz1.Add(save_btn)
        fgz1.Add(stop_btn)
        box.Add(fgz1)
        # 创建文本框，父组件，大小，样式：多行文本框输入，只读
        self.Chat_text = wx.TextCtrl(pl, size=(600, 220), style=wx.TE_MULTILINE | wx.TE_READONLY)
        # 将聊天框放进box中
        box.Add(self.Chat_text, 1, wx.ALIGN_CENTER)
        # 将box放进面板中
        pl.SetSizer(box)
        '''-----------------------服务器启动的必要属性-----------------------'''
        self.isOn= False
        self.host_port =('127.0.0.1',8888)
        #服务器套接字
        self.server_socket = socket(AF_INET,SOCK_STREAM)
        #绑定端口
        self.server_socket.bind(self.host_port)
        # 监听
        self.server_socket.listen(5)
        # 创建一个字典存储与客户端对话的会话线程
        self.session_thread_dict={} # key_value {客户端的名称key:会话线程value}
        # 为事件绑定函数
        self.Bind(wx.EVT_BUTTON,self.start_server,start_server_btn)
        self.Bind(wx.EVT_BUTTON,self.save_log,save_btn)
        self.Bind(wx.EVT_BUTTON,self.stop_server,stop_btn)

    def stop_server(self,event):
        print('服务器已经停止')
        self.isOn=False
        self.server_socket.close()
        #清理字典
        self.session_thread_dict.clear()
        print('服务器已完全停止')


    def save_log(self,event):
        data = self.Chat_text.GetValue()
        with open('log.text','a',encoding='utf-8') as file:
            file.write(data)

    def start_server(self,event):
        if not self.isOn: # 等价于self.isOn==False
            # 启动服务器
            self.isOn =True
            # 创建主线程，
            main_thread = threading.Thread(target=self.do_work)
            # 设置为守护线程
            main_thread.daemon =True
            # 启动主线程
            main_thread.start()

    def do_work(self):
        while self.isOn:
            print(self.isOn)
            # 接受用户的连接请求，新的套接字对象（用来与客户端通信），  客户端的地址信息
            session_socket,client_addr = self.server_socket.accept()
            # 第一次传输的数据是用户的名称
            user_name = session_socket.recv(1024).decode('utf-8')
            # 创建会话线程
            session_thread = Session_thread(session_socket,user_name,self)
            # 添加进字典里面去
            self.session_thread_dict[user_name] = session_thread
            # 开启会话线程
            session_thread.start()
            # 显示信息
            self.show_info_and_send_client('服务器信息：',f'欢迎{user_name}来到聊天室',time.strftime('%Y-%m-%d:%H:%M:%S',localtime()))
        # 如果sion为false，就要关闭server的套接字了
        self.server_socket.close()


    '''
        向服务器显示和向客户端发送信息
    '''
    def show_info_and_send_client(self,data_source,data,data_time):
        # 字符串拼接操作
        send_data = f'{data_source}:{data}\n时间：{data_time}\n'
        # 显示到服务端
        self.Chat_text.AppendText(send_data)
        # 向客户端发送信息
        for client in self.session_thread_dict.values():
            # 这个会话线程是打开的
            if client.isOn:
                # 给服务器法信息
                client.session_socket.send(send_data.encode('utf-8'))



class Session_thread(threading.Thread):
    def __init__(self,session_socket,user_name,server):
        threading.Thread.__init__(self)
        self.session_socket = session_socket
        self.user_name = user_name
        self.server = server
        self.isOn = True

    def run(self):
        print(f'客户端：{self.user_name}已经与服务器连接成功了，服务器启动了一个会话线程')
        while self.isOn:
            data = self.session_socket.recv(1024).decode('utf-8')
            if data =='hhtpassword':
                # 接受到这个的数据表示关闭会话
                self.isOn = False
                self.server.show_info_and_send_client('服务器通知',f'{self.user_name}离开聊天室',time.strftime('%Y-%m-%d:%H:%M:%S',localtime()))
            else:
                # 如果是其他的数据发送给服务器
                self.server.show_info_and_send_client(self.user_name,data,time.strftime('%Y-%m-%d:%H:%M:%S',localtime()))
        # 关闭会话线程的套接字
        self.session_socket.close()
        #清除字典里面的数据
        del self.server.session_thread_dict[self.user_name]
        print(f'{self.user_name}已经被清理')




if __name__ == '__main__':
    app=wx.App()
    server = Hhtserver('hht')
    server.Show()
    app.MainLoop()

